#include "usart_app.h"

#define USART0_TX_BUFFER_SIZE       256
#define USART0_TX_DMA_BLOCK_SIZE    64
#define USART0_RX_BUFFER_SIZE       256

#define USART0_FIFO_SIZE            4

RingBuf_Object usart0_tx_rb, usart0_rx_rb;  // 环形缓存区结构体
uint8_t usart0_tx_buffer[USART0_TX_BUFFER_SIZE] = {0};          // USART0发送缓存区（环形缓存区）
uint8_t usart0_rx_buffer[USART0_RX_BUFFER_SIZE] = {0};          // USART0接收缓存区（环形缓存区）
uint8_t usart0_dma_tx_block[USART0_TX_DMA_BLOCK_SIZE] = {0};    // DMA发送缓存区
volatile bool usart0_dma_busy = false;      // DMA发送空闲状态标志位
volatile uint32_t usart0_rx_timeout = 0;    // 初始为0，uwTick从0开始时需注意溢出，但短时使用可忽略

// USART0 DMA发送函数
void Usart0_Dma_Transfer(void)
{
    if (usart0_dma_busy)
        return;

    // 获取环形缓存区元素数量
    int count = RingBuf_getCount(&usart0_tx_rb);
    if (count == 0)
        return;
    count = count > USART0_TX_DMA_BLOCK_SIZE ? USART0_TX_DMA_BLOCK_SIZE : count;

    // 取出环形缓存区元素，数量为count
    for (int i = 0; i < count; i++)
        RingBuf_get(&usart0_tx_rb, &usart0_dma_tx_block[i]);

    // 关闭DMA发送通道
    DL_DMA_disableChannel(DMA, DMA_CH0_CHAN_ID);
    // 配置DMA发送通道源地址
    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)usart0_dma_tx_block);
    // 配置DMA发送通道一次搬运的元素数量
    DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, (uint16_t)count);
    // DMA发送通道进入忙碌状态
    usart0_dma_busy = true;
    // 开启DMA发送通道
    DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
}

void UART_0_INST_IRQHandler(void)
{
    uint32_t pending = DL_UART_Main_getPendingInterrupt(UART_0_INST);

    switch (pending)
    {
        case DL_UART_MAIN_IIDX_DMA_DONE_TX:
        {
            /* DMA发送通道搬运完成 */
            DL_DMA_clearInterruptStatus(DMA, DL_DMA_INTERRUPT_CHANNEL0);
            usart0_dma_busy = false;
            if (RingBuf_getCount(&usart0_tx_rb) > 0)
                Usart0_Dma_Transfer();
            break;
        }
            
        case DL_UART_IIDX_RX_TIMEOUT_ERROR:
        {
            /* 接收超时 - 处理接收到的数据 */
            DL_UART_Main_clearInterruptStatus(UART_0_INST, UART_0_INST_INT_IRQN);
            usart0_rx_timeout = uwTick;

            uint8_t buffer_temp[USART0_FIFO_SIZE] = {0};
            int count = 0;
            
            count = DL_UART_drainRXFIFO(UART_0_INST, buffer_temp, USART0_FIFO_SIZE);
            for (int i = 0; i < count; i++)
            {
                if (RingBuf_put(&usart0_rx_rb, buffer_temp[i]) < 0)
                    break;
            }
            break;
        }

        case DL_UART_MAIN_IIDX_RX:
        {
            DL_UART_Main_clearInterruptStatus(UART_0_INST, UART_0_INST_INT_IRQN);
            usart0_rx_timeout = uwTick;
            
            uint8_t buffer_temp[USART0_FIFO_SIZE] = {0};
            int count = 0;
            
            count = DL_UART_drainRXFIFO(UART_0_INST, buffer_temp, USART0_FIFO_SIZE);
            for (int i = 0; i < count; i++)
            {
                if (RingBuf_put(&usart0_rx_rb, buffer_temp[i]) < 0)
                    break;
            }
            break;
        }

        default:
            DL_UART_Main_clearInterruptStatus(UART_0_INST, UART_0_INST_INT_IRQN);
            DL_DMA_clearInterruptStatus(DMA, DL_DMA_INTERRUPT_CHANNEL0);
            break;
    }
}


// USART0格式化字符串打印
void Usart0_Printf(const char *format, ...)
{
    char buffer[USART0_TX_BUFFER_SIZE];
    va_list arg;
    int len;

    va_start(arg, format);
    len = vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);
    if (len <= 0)
        return;

    for (int i = 0; i < len; i++)
    {
        // 缓冲区填满会直接跳出，避免阻塞
        if (RingBuf_isFull(&usart0_tx_rb))
            break;
        RingBuf_put(&usart0_tx_rb, (uint8_t)buffer[i]);
    }
    if (!usart0_dma_busy)
        Usart0_Dma_Transfer();
}

void Usart0_Task(void)
{
    int count = RingBuf_getCount(&usart0_rx_rb);

    if (count > 0 && uwTick - usart0_rx_timeout >= 5)
    {
        uint8_t buffer[USART0_RX_BUFFER_SIZE] = {0};

        for (int i = 0; i < count; i++)
            RingBuf_get(&usart0_rx_rb, &buffer[i]);

        Usart0_Printf("buffer: %s\r\n", buffer);
    }
}

// USART0初始化
void Usart0_Init(void)
{
    // 禁用TX DMA通道（初始状态）
    DL_DMA_disableChannel(DMA, DMA_CH0_CHAN_ID);

    // 环形缓存区初始化
    RingBuf_construct(&usart0_tx_rb, usart0_tx_buffer, USART0_TX_BUFFER_SIZE);
    RingBuf_construct(&usart0_rx_rb, usart0_rx_buffer, USART0_RX_BUFFER_SIZE);

    // 配置DMA发送通道目标地址
    DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&(UART_0_INST -> TXDATA));

    // 清除串口中断标志
    NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
    // 使能串口中断
    NVIC_EnableIRQ(UART_0_INST_INT_IRQN);

    Usart0_Printf("USART0 Init OK\r\n");
}
